/*
 * @Author: Swance
 * @Date: 2023-12-14
 * @Description: 线条插件
 */

import { v4 as uuid } from 'uuid';
import { fabric } from 'fabric';
import { IEditor } from '@/types/global';
import Arrow from '@/utils/Arrow.ts';
import { BoardSyncEvent, JsonOption } from '@/utils/Const.ts';

class DrawLinePlugin {
  public canvas: fabric.Canvas;
  public editor: IEditor;
  static pluginName = 'DrawLinePlugin';
  static apis = [
    'setIsArrow',
    'setLineMode',
    'setLineColor',
    'setLineWidth',
    'setLineStyle',
  ];
  isDrawingLineMode: boolean; // 画线模式
  isArrow: boolean; // 是否箭头
  lineToDraw: any; //画出来的线条对象
  isDrawingLine: boolean; // 是否正在画线
  pointer: any;
  pointerPoints: any;

  constructor(canvas: fabric.Canvas, editor: IEditor) {
    this.canvas = canvas;
    this.editor = editor;
    this.isDrawingLine = false;
    this.isDrawingLineMode = false;
    this.isArrow = false;
    this.lineToDraw = null;
    this.pointer = null;
    this.pointerPoints = null;
    this.init();
  }

  // 为画布添加事件：鼠标按下、鼠标移动、鼠标松开
  init() {
    const { canvas } = this;
    // 鼠标按下时创建 line 或 arrow 对象
    canvas.on('mouse:down', (e) => {
      if (!this.isDrawingLineMode || e.button === 3) return;
      // 正在画线
      this.isDrawingLine = true;
      // 返回一个包含当前鼠标位置和相对于画布的 X 和 Y 坐标的对象
      this.pointer = canvas.getPointer(e.e);
      this.pointerPoints = [
        this.pointer.x,
        this.pointer.y,
        this.pointer.x,
        this.pointer.y,
      ];
      const lineObject = this.isArrow ? Arrow : fabric.Line;
      this.lineToDraw = new lineObject(this.pointerPoints, {
        strokeWidth: 2,
        stroke: 'rgba(0, 0, 0, 1)',
        id: uuid(),
        lock: false,
        perPixelTargetFind: true,
        erasable: false, // 不允许擦拭
      });
      // 禁止选中该线条对象
      this.lineToDraw.selectable = false;
      //  禁止线条对象的点击事件
      this.lineToDraw.evented = false;
      // 限制元素边框宽度缩放
      this.lineToDraw.strokeUniform = true;
      canvas.add(this.lineToDraw);
    });

    // 鼠标移动时画线
    canvas.on('mouse:move', (e) => {
      if (!this.isDrawingLine) return;
      // 取消画布中所有元素的选中状态
      canvas.discardActiveObject();
      // 获得画布上的活动对象
      const activeObject = canvas.getActiveObject();
      if (activeObject) return;
      // 获取鼠标当前的坐标（获得的坐标和画布真实坐标可能不同）
      this.pointer = canvas.getPointer(e.e);
      // 设置线条终点位置
      this.lineToDraw.set({
        x2: this.pointer.x,
        y2: this.pointer.y,
      });
      // 立即重新渲染画布，同步
      canvas.renderAll();
    });

    // 鼠标松开，结束正在画线状态
    canvas.on('mouse:up', () => {
      if (!this.isDrawingLine) return;
      // 重新计算控制位置
      this.lineToDraw.setCoords();
      this.isDrawingLine = false;
      // 取消画布中所有元素的选中状态
      canvas.discardActiveObject();
      this.editor.emit(BoardSyncEvent.ADD, {
        isNewAction: true,
        newElements: [this.lineToDraw],
      });
    });
  }

  // 设置是否为箭头
  setIsArrow(params: boolean) {
    this.isArrow = params;
  }

  // 设置模式
  setLineMode(params: boolean) {
    this.isDrawingLineMode = params;
    if (!this.isDrawingLineMode) {
      this.resume();
    } else {
      const { canvas } = this;
      canvas.selection = false;
      canvas.discardActiveObject();
      canvas.getObjects().forEach((obj) => {
        // 禁止选中画布元素
        obj.selectable = false;
        // 隐藏画布元素的控制角，使其旋转、缩放
        obj.hasControls = false;
        obj.evented = false;
      });
      // 异步重绘画布内容，它会在下一次浏览器的重绘周期中重新渲染画布
      canvas.requestRenderAll();
    }
  }

  // 恢复画布中所有元素的可选择和可点击
  resume() {
    this.canvas.selection = true;
    this.canvas.getObjects().forEach((obj) => {
      obj.selectable = true;
      obj.hasControls = true;
      obj.evented = true;
    });
  }

  getActiveLineObject() {
    const activeObject = this.canvas.getActiveObject();
    if (
      activeObject &&
      (activeObject.type === 'arrow' || activeObject.type === 'line')
    ) {
      return activeObject;
    }
    return null;
  }

  setLineStyle(type: string) {
    const activeObject = this.getActiveLineObject();
    if (!activeObject) return;
    const oldElementJson = activeObject.toJSON(JsonOption);
    switch (type) {
      case 'line':
        activeObject.strokeDashArray = [];
        break;
      case 'dash':
        activeObject.strokeDashArray = [15, 12];
        break;
      case 'smallDash':
        activeObject.strokeDashArray = [6, 8];
        break;
    }
    activeObject.dirty = true;
    const newElementJson = activeObject.toJSON(JsonOption);
    this.editor.emit(BoardSyncEvent.UPDATE, {
      isNewAction: true,
      oldElements: [oldElementJson],
      newElements: [newElementJson],
    });
    this.canvas.requestRenderAll();
  }

  setLineWidth(width: number) {
    const activeObject = this.getActiveLineObject();
    if (!activeObject) return;
    const oldElementJson = activeObject.toJSON(JsonOption);
    activeObject.strokeWidth = width;
    activeObject.dirty = true;
    const newElementJson = activeObject.toJSON(JsonOption);
    this.editor.emit(BoardSyncEvent.UPDATE, {
      isNewAction: true,
      oldElements: [oldElementJson],
      newElements: [newElementJson],
    });
    this.canvas.requestRenderAll();
  }

  setLineColor(color: string) {
    const activeObject = this.getActiveLineObject();
    if (!activeObject) return;
    const oldElementJson = activeObject.toJSON(JsonOption);
    activeObject.stroke = color;
    activeObject.dirty = true;
    const newElementJson = activeObject.toJSON(JsonOption);
    this.editor.emit(BoardSyncEvent.UPDATE, {
      isNewAction: true,
      oldElements: [oldElementJson],
      newElements: [newElementJson],
    });
    this.canvas.requestRenderAll();
  }
}
export default DrawLinePlugin;
